/*

=============================================================================

Copyright (c) 2010 Xiotex Studios Ltd.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The end-user documentation included with the redistribution, if any, must
include the following acknowledgment: "This product includes software
developed by Xiotex Studios Ltd (http://xiotexstudios.com) and Oladotun Rominiy",
in the same place and form as other third-party acknowledgments. Alternately,
this acknowledgment may appear in the software itself, in the same form and
location as other such third-party acknowledgments.
 
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. 

=============================================================================

File:		CSWFStream.cpp
Author:		Byron Atkinson-Jones

Update(s):	(Ola)Dotun Rominiyi (28/10/2010) - replaced built-in types for
											   basic types
										     - shuffled includes for better
											   compilation times
										     - switched initialisation to
											   implement the CSWFAbstractStream
											 - switched printf's to use CSWFLOG

=============================================================================

*/

#include "SWFBase.h"
#include "SWFLog.h"
#include "CSWFStream.h"
#include "CSWFTagList.h"

#include "CSWFAbstractStream.h"


CSWFStream::CSWFStream()
{
	m_pIn = NULL;
	m_pBuffer = NULL;
}

CSWFStream::~CSWFStream()
{
	ShutDown();
}

bool CSWFStream::Initialise(CSWFAbstractStream* pSourceStream)
{
	uInt32 dwLength;
	uInt32 pos;

	m_pIn = pSourceStream;
	
	bool success = m_pIn->Open(eOpenMode_Read);
	if(!success)
	{
		return false;
	}

	m_pIn->Seek(0,eSeekMode_End);

	pos = m_pIn->Tell();
	
	m_pBuffer = _CSWFNEW uChar8[pos];
	if(!m_pBuffer)
	{
		m_pIn->Close();
		return false;
	}

	m_pIn->Seek(0,eSeekMode_Start);
	dwLength = m_pIn->Read(m_pBuffer,pos,sizeof(uChar8));
	m_pIn->Close();

	if(dwLength != pos)
	{
		return false;
	}
	
	m_dwLength = dwLength;

	return true;
}

void CSWFStream::ShutDown()
{
	if(m_pBuffer)
	{
		_CSWFDELETEARRAY( m_pBuffer );
		m_pBuffer = NULL;
	}
}

void CSWFStream::StartStream(swf_stream pStream)
{
	m_pStream = pStream;
	m_pStreamStart = pStream;
	m_dwNumBytes = 0;
	m_dwBitBuffer = 0;
	m_dwBitPosition = 0;
}

sInt32 CSWFStream::GetSignedBits(uInt32 dwNumBits)
{
	sInt32 lValue = static_cast<sInt32>(GetBits(dwNumBits));

	if(lValue & (1L << (dwNumBits -1 )))
	{
		lValue |= -1L << dwNumBits;
	}

	return lValue;
}

uInt32 CSWFStream::GetBits(uInt32 dwNumBits)
{
	uInt32 dwValue = 0;
	sInt32 dwBitPosition;

	while(1)
	{
		dwBitPosition = dwNumBits - m_dwBitPosition;

		if(dwBitPosition > 0)
		{
			// We want the whole of the buffer
			dwValue |= m_dwBitBuffer << dwBitPosition;
			dwNumBits -= m_dwBitPosition;

			// Get next byte
			m_dwBitBuffer = *(m_pStream++);
			m_dwBitPosition = 8;
			m_dwNumBytes = static_cast<uInt32>(m_pStream - m_pStreamStart);
		}
		else
		{
			// We only want a bit of the bit buffer
			dwValue |= m_dwBitBuffer >> -dwBitPosition;
			m_dwBitPosition -= dwNumBits;
			m_dwBitBuffer &= 0xff >> (8 - m_dwBitPosition);

			return dwValue;
		}
	}
	
	return 0xFFFFFFFF;
}

uChar8 CSWFStream::GetBYTE(swf_stream pStream)
{
	return *pStream;
}



uInt32 CSWFStream::GetWORD(swf_stream pStream)
{
	uInt32 dwValue;
	
	dwValue = static_cast<uInt32>(pStream[0]) | (static_cast<uInt32>(pStream[1]) << 8);

	
	return dwValue;
	
}

uInt32 CSWFStream::GetInt(swf_stream pStream)
{
	uInt32 dwValue;

	dwValue = static_cast<uInt32>(pStream[0]) | 
			  (static_cast<uInt32>(pStream[1]) << 8) |
			  (static_cast<uInt32>(pStream[2]) << 16) |
			  (static_cast<uInt32>(pStream[3]) << 24);

	return dwValue;
}

void CSWFStream::GetShortTag(PSWF_TAG_SHORT pTag, swf_stream pStream)
{
	uInt32 dwValue;

	dwValue = GetWORD(pStream);

	pTag->m_dwTagID = (dwValue >> 6);
	pTag->m_dwLength = dwValue & 0x3f;
}

void CSWFStream::GetIntTag(PSWF_TAG_sInt32 pTag, swf_stream pStream)
{
	uInt32 dwValue;

	dwValue = GetWORD(pStream);

	pTag->m_dwTagID = (dwValue >> 6);
	pTag->m_dwLength16 = dwValue & 0x3f;
	pTag->m_dwLength = GetInt(pStream+2);
}

uInt32 CSWFStream::GetRect(PSWF_RECT rect, swf_stream pStream)
{
	uInt32	dwNumBits;

	StartStream(pStream);
	dwNumBits = GetBits(SWF_RECT_HEADER_SIZE);
	rect->m_dwXMin = GetSignedBits(dwNumBits);
	rect->m_dwXMax = GetSignedBits(dwNumBits);
	rect->m_dwYMin = GetSignedBits(dwNumBits);
	rect->m_dwYMax = GetSignedBits(dwNumBits);

	return m_dwNumBytes;
}

void CSWFStream::ExtractMatrix(PSWF_MATRIX_RECORD pMatrix, swf_stream *pStream)
{
	uInt32 dwNumBits;
	uInt32 dwScale;
	uInt32 dwSkew;

	StartStream(*pStream);

	dwScale = GetBits(1);

	if(dwScale == 1)
	{
		dwNumBits = GetBits(5);

		pMatrix->m_fScaleX = (static_cast<float32>(GetSignedBits(dwNumBits)));
		pMatrix->m_fScaleY = (static_cast<float32>(GetSignedBits(dwNumBits)));
		pMatrix->m_fScaleX /= 65536.0f;
		pMatrix->m_fScaleY /= 65536.0f;

	}
	else
	{
		pMatrix->m_fScaleX = 1.0f;
		pMatrix->m_fScaleY = 1.0f;
	}

	dwSkew = GetBits(1);

	if(dwSkew == 1)
	{
		dwNumBits = GetBits(5);

		pMatrix->m_fRotateSkew0 = (static_cast<float32>(GetSignedBits(dwNumBits)));
		pMatrix->m_fRotateSkew1 = (static_cast<float32>(GetSignedBits(dwNumBits)));
		pMatrix->m_fRotateSkew0 /= 65536.0f;
		pMatrix->m_fRotateSkew1 /= 65536.0f;
	}
	else
	{
		pMatrix->m_fRotateSkew0 = 0.0f;
		pMatrix->m_fRotateSkew1 = 0.0f;

	}

	dwNumBits = GetBits(5);

	pMatrix->m_fTranslateX = static_cast<float32>(GetSignedBits(dwNumBits));
	pMatrix->m_fTranslateY = static_cast<float32>(GetSignedBits(dwNumBits));

	(*pStream) += GetNumBytesRead();
}




void CSWFStream::ExtractRGBA(PSWF_RGBA pRGBA, swf_stream *pStream)
{
	pRGBA->m_R = GetBYTE(*pStream);
	(*pStream) += SWF_SKIP_BYTE;
	pRGBA->m_G = GetBYTE(*pStream);
	(*pStream) += SWF_SKIP_BYTE;
	pRGBA->m_B = GetBYTE(*pStream);
	(*pStream) += SWF_SKIP_BYTE;
	pRGBA->m_A = GetBYTE(*pStream);
	(*pStream) += SWF_SKIP_BYTE;

}

void CSWFStream::ExtractRGB(PSWF_RGBA pRGB, swf_stream *pStream)
{
	pRGB->m_R = GetBYTE(*pStream);
	(*pStream) += SWF_SKIP_BYTE;
	pRGB->m_G = GetBYTE(*pStream);
	(*pStream) += SWF_SKIP_BYTE;
	pRGB->m_B = GetBYTE(*pStream);
	(*pStream) += SWF_SKIP_BYTE;
	pRGB->m_A = 0xff;

}



void CSWFStream::ExtractColourTransformRGBA(PSWF_COLOUR_TRANSFORM pRGBA, swf_stream *pStream)
{
	uInt32 dwHasAddTerms;
	uInt32 dwHasMultTerms;
	uInt32 dwNumBits;

	StartStream(*pStream);
	dwHasAddTerms = GetBits(1);
	dwHasMultTerms = GetBits(1);
	dwNumBits = GetBits(4);
	pRGBA->m_lRMult = 0;
	pRGBA->m_lGMult = 0;
	pRGBA->m_lBMult = 0;
	pRGBA->m_lAMult = 0;
	pRGBA->m_lRAdd = 0;
	pRGBA->m_lGAdd = 0;
	pRGBA->m_lBAdd = 0;
	pRGBA->m_lAAdd = 0;

	if(dwHasMultTerms == 1)
	{
		pRGBA->m_lRMult = GetSignedBits(dwNumBits);
		pRGBA->m_lGMult = GetSignedBits(dwNumBits);
		pRGBA->m_lBMult = GetSignedBits(dwNumBits);
		pRGBA->m_lAMult = GetSignedBits(dwNumBits);
	}

	if(dwHasAddTerms == 1)
	{
		pRGBA->m_lRAdd = GetSignedBits(dwNumBits);
		pRGBA->m_lGAdd = GetSignedBits(dwNumBits);
		pRGBA->m_lBAdd = GetSignedBits(dwNumBits);
		pRGBA->m_lAAdd = GetSignedBits(dwNumBits);
	}
}

void CSWFStream::ExtractColourTransformRGB(PSWF_COLOUR_TRANSFORM pRGBA, swf_stream *pStream)
{
	uInt32 dwHasAddTerms;
	uInt32 dwHasMultTerms;
	uInt32 dwNumBits;

	StartStream(*pStream);
	dwHasAddTerms = GetBits(1);
	dwHasMultTerms = GetBits(1);
	dwNumBits = GetBits(4);


	if(dwHasMultTerms == 1)
	{
		pRGBA->m_lRMult = GetSignedBits(dwNumBits);
		pRGBA->m_lGMult = GetSignedBits(dwNumBits);
		pRGBA->m_lBMult = GetSignedBits(dwNumBits);
		pRGBA->m_lAMult = 0;
	}

	if(dwHasAddTerms == 1)
	{
		pRGBA->m_lRAdd = GetSignedBits(dwNumBits);
		pRGBA->m_lGAdd = GetSignedBits(dwNumBits);
		pRGBA->m_lBAdd = GetSignedBits(dwNumBits);
		pRGBA->m_lAAdd = 0;
	}

	(*pStream) += GetNumBytesRead();

}

void CSWFStream::ExtractPlaceObject2(PSWF_TAG pTag, swf_stream pStream)
{
	uInt32 dwHasClipActions;
	uInt32 dwHasClipDepth;
	uInt32 dwHasName;
	uInt32 dwHasRatio;
	uInt32 dwHasColourTransform;
	uInt32 dwHasMatrix;
	uInt32 dwHasCharacter;
	uInt32 dwPlaceMove;
	

	CSWFLOG("PlaceObject2 ");


	StartStream(pStream);

	dwHasClipActions = GetBits(1);
	dwHasClipDepth = GetBits(1);
	dwHasName = GetBits(1);
	dwHasRatio = GetBits(1);
	dwHasColourTransform = GetBits(1);
	dwHasMatrix = GetBits(1);
	dwHasCharacter = GetBits(1);
	dwPlaceMove = GetBits(1);

	pStream += GetNumBytesRead();

	pTag->m_iDepth = GetWORD(pStream);
	pStream += SWF_SKIP_WORD;

	pTag->m_bHasCharacter = false;
	pTag->m_bHasClipActions = false;
	pTag->m_bHasClipDepth = false;
	pTag->m_bHasColourTransform = false;
	pTag->m_bHasMatrix = false;
	pTag->m_bHasMove = false;
	pTag->m_bHasName = false;
	pTag->m_bHasRatio = false;
	pTag->m_bSetParent = false;

	// Get the character ID
	if(dwHasCharacter == 1)
	{
		pTag->m_bHasCharacter = true;
		pTag->m_dwID = GetWORD(pStream);
		pStream += SWF_SKIP_WORD;
		
		CSWFLOG("ID: %d ",pTag->m_dwID);
	}
	else
	{
		pTag->m_dwID = 0xFFFFFFFF;
	}


	if(dwHasMatrix == 1)
	{
		pTag->m_bHasMatrix = true;
		ExtractMatrix(&pTag->m_mMatrix,&pStream);		
		CSWFLOG("X: %f Y: %f ScaleX : %f ScaleY: %f RotateSkew0: %f RotateSkew1: %f ",pTag->m_mMatrix.m_fTranslateX / SWF_TWIPS,pTag->m_mMatrix.m_fTranslateY / SWF_TWIPS,pTag->m_mMatrix.m_fScaleX,pTag->m_mMatrix.m_fScaleY,pTag->m_mMatrix.m_fRotateSkew0,pTag->m_mMatrix.m_fRotateSkew1);

		SetMatrix(pTag->m_mMatrix.m_fScaleX, pTag->m_mMatrix.m_fScaleY, pTag->m_mMatrix.m_fRotateSkew0, pTag->m_mMatrix.m_fRotateSkew1, pTag->m_mMatrix.m_fTranslateX / SWF_TWIPS, pTag->m_mMatrix.m_fTranslateY / SWF_TWIPS);
		
		pTag->m_fScaleX = GetScaleX();
		pTag->m_fScaleY = GetScaleY();
		pTag->m_fAngle = GetAngle();
		pTag->m_fTX = GetTranslateX();
		pTag->m_fTY = GetTranslateY();
	}

	if(dwHasColourTransform == 1)
	{
		pTag->m_bHasColourTransform = true;
		ExtractColourTransformRGBA(&pTag->m_ColourTransform,&pStream);
		
		CSWFLOG("Alpha: %f ",(float32)(pTag->m_ColourTransform.m_lAMult) / 255.0f);
	}

	if(dwHasRatio == 1)
	{
		pTag->m_bHasRatio = true;
		pTag->m_dwRatio = GetWORD(pStream);
		pStream += SWF_SKIP_WORD;
	}

	if(dwHasName == 1)
	{
		pTag->m_bHasName = true;
		pTag->m_strName = reinterpret_cast<char*>(pStream);
		pStream += strlen(reinterpret_cast<char*>(pStream)) + 1;
		
		CSWFLOG("Name: %s ",pTag->m_strName.c_str());
	}

	if(dwHasClipDepth == 1)
	{
		pTag->m_dwClipDepth = GetWORD(pStream);
		pStream += SWF_SKIP_WORD;
	}
	
	if(dwPlaceMove == 1)
	{
		pTag->m_bHasMove = true;
	}
	
	CSWFLOG("Depth: %d ",pTag->m_iDepth);
	CSWFLOG("\n");
}

void CSWFStream::ExtractRemoveObject(PSWF_TAG pTag, swf_stream pStream)
{
	StartStream(pStream);

	pTag->m_iDepth = GetWORD(pStream);
	
	CSWFLOG("RemoveObject2: %d\n",pTag->m_iDepth);
}

void CSWFStream::ExtractDefineShape(PSWF_TAG pTag, swf_stream pStream)
{
	StartStream(pStream);
	
	pTag->m_dwID = GetWORD(pStream);
	pStream += SWF_SKIP_WORD;
	
	GetRect(&pTag->m_sRect,pStream);
	pTag->m_sRect.m_dwXMin /= SWF_TWIPS;
	pTag->m_sRect.m_dwYMin /= SWF_TWIPS;
	pTag->m_sRect.m_dwXMax /= SWF_TWIPS;
	pTag->m_sRect.m_dwYMax /= SWF_TWIPS;
	
}

void CSWFStream::ExtractDefineSprite(PSWF_TAG pTag, swf_stream pStream, CSWFTagList* pTagList)
{
	StartStream(pStream);
	
	pTag->m_dwID = GetWORD(pStream);
	pStream += SWF_SKIP_WORD;	
	
	uInt32 dwFrameCount = GetWORD(pStream);
	pStream += SWF_SKIP_WORD;	
	
	if(dwFrameCount == 0)
	{
		return;
	}
	
	SWF_TAG_SHORT ShortTag;
	SWF_TAG_sInt32 sInt32Tag;
	SWF_TAG Tag;
	
	Tag.m_dwType = 0xFFFFFFFF;
	
	GetShortTag(&ShortTag,pStream);
	
	if(ShortTag.m_dwLength == 0x3f)
	{
		GetIntTag(&sInt32Tag,pStream);
		Tag.m_dwType = sInt32Tag.m_dwTagID;
		Tag.m_dwLength = sInt32Tag.m_dwLength;
		Tag.m_pStream = pStream + SWF_sInt32_TAG_HEADER_LENGTH;
	}
	else
	{
		Tag.m_dwType = ShortTag.m_dwTagID;
		Tag.m_dwLength = ShortTag.m_dwLength;
		Tag.m_pStream = pStream + SWF_SHORT_TAG_HEADER_LENGTH;
	}
	
	ExtractPlaceObject2(&Tag, Tag.m_pStream);		

	uInt32 i;
	PSWF_TAG pTag1;
	
	for(i = 0; i < static_cast<uInt32>(pTagList->m_vTags.size()); i++)
	{
		pTag1 = &pTagList->m_vTags[i];
		
		switch(pTag1->m_dwType)
		{
			case SWF_PLACE_OBJECT_2:
				if(pTag1->m_dwID == pTag->m_dwID && !pTag1->m_bSetParent)
				{
					pTag1->m_fParentScaleX = Tag.m_fScaleX;
					pTag1->m_fParentScaleY = Tag.m_fScaleY;
					pTag1->m_fParentAngle = Tag.m_fAngle;
					pTag1->m_fParentTX = Tag.m_fTX;
					pTag1->m_fParentTY = Tag.m_fTY;
					pTag1->m_bSetParent = true;
					
					PSWF_TAG pTag2;
					uInt32 j;
					
					for(j = 0; j < static_cast<uInt32>(pTagList->m_vTags.size()); j++)
					{
						pTag2 = &pTagList->m_vTags[j];
						
						if(pTag2->m_dwType == SWF_DEFINE_SHAPE)
						{
							if(pTag2->m_dwID == Tag.m_dwID)
							{
								pTag1->m_fWidth  = (float32)pTag2->m_sRect.m_dwXMax;
								pTag1->m_fHeight  = (float32)pTag2->m_sRect.m_dwYMax;
								
							}
						}
					}						
					
				}
				break;
		}
	}
}

